'''
Created on Jun 4, 2010

@author: roni
'''
from generators.randomScenario import RandomScenario
from ns2Scenario import Ns2Scenario
import random
import logging
from abnormalScenarioGenerator import AbnormalScenarioGenerator
from diagnosisEnvironment import DiagnosisEnvironment
from projectConstants import ProjectConstants
from ns2Observations import Ns2Observations

class EnvironmentGenerator(object):
    '''
    Generates environments from diagnosis experiments
    '''
    ABNORMAL_ADDED_DELAY = Ns2Scenario.LINK_DELAY*1000

    def __init__(self):
        self.scenario_generator = RandomScenario()
        self.abnormality_generator = AbnormalScenarioGenerator()
        
    def generate(self,nodes, added_edges, agents,abnormal_probability,id):    
        ''' Generates a random environment '''
        scenario = self.scenario_generator.generate(nodes, added_edges, agents)        
        node_to_delay = self.choose_abnormals(scenario, abnormal_probability)
        
        abnormals = node_to_delay.keys()         
        logging.info("Generating environment with %d abnormals [%s]" % (len(abnormals),abnormals))
        observed_flow_to_details = self.run_abnormal_simulation(scenario, node_to_delay)
        observations = Ns2Observations(observed_flow_to_details)
                
        environment = DiagnosisEnvironment()
        environment.scenario = scenario
        environment.observations = observations
        environment.added_delays = node_to_delay
        return environment
                
    def choose_abnormals(self,scenario, normal_probability):
        ''' Add random delays to nodes on agents paths. At least one node will be added,
        other nodes on paths will be added according to the abnormal probability (i.i.d) '''    
        possible_abnormals = set()
        # Abnormal candidates: all nodes on the paths of the agents
        for agent in scenario.agents:
            possible_abnormals.update(scenario.agent_to_path[agent])
            
        # Current GDE focuses on abnormals in the paths
        for agent in scenario.agents:
            possible_abnormals.difference_update(scenario.agent_to_flow[agent])
           
        # Make at least one node to be abnormal
        added_delay = EnvironmentGenerator.ABNORMAL_ADDED_DELAY
        node_to_delay = dict()
        possible_abnormals = list(possible_abnormals)
        abnormal_index = random.randint(0,len(possible_abnormals)-1)
        abnormal = possible_abnormals[abnormal_index]
        node_to_delay[abnormal] = added_delay
        logging.info("Added %d ms delay to node %s" % (added_delay,abnormal))    
        possible_abnormals.remove(abnormal)
        
        # Add abnormals according to the given probabilty
        for node in possible_abnormals:
            if random.random()>normal_probability:
                node_to_delay[node]=added_delay
                logging.info("Added %d ms delay to node %s" % (added_delay,node))
        
        return node_to_delay
    
    def run_abnormal_simulation(self,scenario, node_to_delay, abnormal_scenario_file="temp.scenario",\
                                abnormal_trace_file = "temp.abnormal.tr"):
        ''' Create an abnormal scenario and run, and return the resulting observations (flow_to_details) '''
        # Modify original scenario                 
        abnormal_scenario_full_path = "%s/%s" % (ProjectConstants.RESROUCE_DIR,abnormal_scenario_file)
        self.abnormality_generator.generateScenario(scenario, abnormal_scenario_full_path, node_to_delay)        

        # Run simulation on abnormal scenario to create trace
        abnormal_scenario = Ns2Scenario(abnormal_scenario_full_path, simulate=False)
        return abnormal_scenario.simulate()   
 
   
     